﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace Gmantis.Uploader
{
    public class UploadManagerPost : IUploadManager
    {
        // Fields
        private string _boundary;
        private bool _hasAborted;
        private UploadFilePartPackage _package;
        private Dictionary<string, string> _parameters;
        private HttpWebRequest _request;
        private UploadManagerSettingsPost _settings;

        // Events
        public event EventHandler<UploadCompletedEventArgs> UploadCompleted;
        public event EventHandler<UploadProgressChangedEventArgs> UploadProgressChanged;

        // Methods
        public bool BeginUpload(UploadManagerSettings settings, UploadFilePartPackage package, Dictionary<HttpRequestHeader, string> headers, Dictionary<string, string> parameters)
        {
            this._settings = settings as UploadManagerSettingsPost;
            this._boundary = (this._settings.Boundary == null) ? this.GetBoundary() : this._settings.Boundary;
            this._package = package;
            this._parameters = parameters;
            try
            {
                this._request = (HttpWebRequest)WebRequest.Create(this._settings.Address);
                this._hasAborted = false;
                foreach (KeyValuePair<HttpRequestHeader, string> pair in headers)
                {
                    this._request.Headers[pair.Key] = pair.Value;
                }
            }
            catch (NotSupportedException exception)
            {
                throw new InvalidAddressException(exception);
            }
            this._request.Method = "POST";
            this._request.ContentType = "multipart/form-data; boundary=" + this._boundary;
            this._request.BeginGetRequestStream(new AsyncCallback(this.RequestProceed), null);
            return true;
        }

        public void Cancel()
        {
            if (this._request != null)
            {
                lock (this._request)
                {
                    this._hasAborted = true;
                    this._request.Abort();
                }
            }
        }

        private byte[] ConvertToByteArray(string s)
        {
            return Encoding.UTF8.GetBytes(s);
        }

        private string GetBoundary()
        {
            int num = 20;
            Random random = new Random();
            char[] chArray = new char[num];
            for (int i = 0; i < num; i++)
            {
                chArray[i] = (char)(random.Next(10) + 0x30);
            }
            return new string(chArray);
        }

        private void OnUploadCompleted(bool success, bool cancelled, Exception exc, string response)
        {
            if (this.UploadCompleted != null)
            {
                this.UploadCompleted(this, new UploadCompletedEventArgs(success, cancelled, exc, null, response, null));
            }
        }

        private void RequestProceed(IAsyncResult asyncResult)
        {
            if (!this._hasAborted)
            {
                BinaryWriter writer;
                try
                {
                    writer = new BinaryWriter(this._request.EndGetRequestStream(asyncResult), Encoding.UTF8);
                }
                catch (Exception exception)
                {
                    this.OnUploadCompleted(false, false, new Exception("There was an error trying to get the request stream", exception), string.Empty);
                    return;
                }
                try
                {
                    foreach (KeyValuePair<string, string> pair in this._parameters)
                    {
                        writer.Write(this.ConvertToByteArray("\r\n--" + this._boundary + "\r\n"));
                        writer.Write(this.ConvertToByteArray("Content-Disposition: form-data; name=\"" + pair.Key + "\"\r\n\r\n"));
                        writer.Write(this.ConvertToByteArray(pair.Value));
                    }
                    int num = 0;
                    foreach (UploadFilePartInfo info in this._package)
                    {
                        if (this._package.ServerWaitForOneFile)
                        {
                            writer.Write(this.ConvertToByteArray("\r\n--" + this._boundary + "\r\n"));
                            writer.Write(this.ConvertToByteArray("Content-Disposition: form-data; name=\"partCount\"\r\n\r\n"));
                            writer.Write(this.ConvertToByteArray(info.PartCount.ToString()));
                            writer.Write(this.ConvertToByteArray("\r\n--" + this._boundary + "\r\n"));
                            writer.Write(this.ConvertToByteArray("Content-Disposition: form-data; name=\"partNumber\"\r\n\r\n"));
                            writer.Write(this.ConvertToByteArray(info.PartNumber.ToString()));
                        }
                        writer.Write(this.ConvertToByteArray("\r\n--" + this._boundary + "\r\n"));
                        writer.Write(this.ConvertToByteArray(string.Concat(new object[] { "Content-Disposition: form-data; name=\"file", num, "\"; filename=\"", info.Name, "\"\r\n" })));
                        if (this._settings.TransferEncoding == MultipartPostEncoding.Base64)
                        {
                            writer.Write(this.ConvertToByteArray("Content-Type: application/x-base64\r\n"));
                            writer.Write(this.ConvertToByteArray("\r\n"));
                            string s = Convert.ToBase64String(info.Data);
                            writer.Write(this.ConvertToByteArray(s));
                        }
                        else
                        {
                            string str2 = this.Uploader.OnMIMETypeRequested(System.IO.Path.GetExtension(info.Name).ToLower());
                            if (!string.IsNullOrEmpty(str2))
                            {
                                writer.Write(this.ConvertToByteArray("Content-Type: " + str2 + "\r\n"));
                            }
                            writer.Write(this.ConvertToByteArray("\r\n"));
                            writer.Write(info.Data, 0, info.Data.Length);
                        }
                        num++;
                    }
                    writer.Write(this.ConvertToByteArray("\r\n--" + this._boundary + "--\r\n"));
                }
                catch (Exception exception2)
                {
                    this.OnUploadCompleted(false, false, new Exception("There was an error reading the files", exception2), string.Empty);
                    return;
                }
                finally
                {
                    writer.Close();
                }
                try
                {
                    this._request.BeginGetResponse(new AsyncCallback(this.ResponseProceed), null);
                }
                catch (OutOfMemoryException exception3)
                {
                    this.OnUploadCompleted(false, false, new Exception("There was an error trying to send the upload request", exception3), string.Empty);
                }
            }
        }

        private void ResponseProceed(IAsyncResult asyncResult)
        {
            if (!this._hasAborted)
            {
                if (!this._request.HaveResponse)
                {
                    this.OnUploadCompleted(false, true, null, string.Empty);
                }
                else
                {
                    try
                    {
                        HttpWebResponse response = (HttpWebResponse)this._request.EndGetResponse(asyncResult);
                        Exception exc = null;
                        bool success = true;
                        string message = string.Empty;
                        Stream responseStream = response.GetResponseStream();
                        if (responseStream != null)
                        {
                            StreamReader reader = new StreamReader(responseStream);
                            message = reader.ReadToEnd();
                            reader.Close();
                        }
                        if (response.StatusCode == HttpStatusCode.NotFound)
                        {
                            success = false;
                            if (responseStream != null)
                            {
                                exc = new Exception(message);
                            }
                            else
                            {
                                exc = new Exception(string.Format("Internal Server Error in {0}.\n\nThe current Silverlight implementation doesn't allow to provide more detailed errors in POST method.", this._request.RequestUri));
                            }
                        }
                        this.OnUploadCompleted(success, false, exc, message);
                    }
                    catch (Exception)
                    {
                        this.OnUploadCompleted(false, false, new Exception(string.Format("Internal Server Error in {0}.\n\nThe current Silverlight implementation doesn't allow to provide more detailed errors in POST method.", this._request.RequestUri)), string.Empty);
                    }
                }
            }
        }

        // Properties
        internal UploaderPost Uploader { get; set; }
    }
}
